#include <QHostAddress>
#include <QThread>
#include "transferworker.h"
#include "datadefine.h"

#define PACKAGE_SIZE_MAX 1448

TransferWorker::TransferWorker(QObject *parent) : QObject(parent)
{
    tcpSocket = nullptr;
    uploadFlag = 0;
    index = -1;
    useSsl = false;
    sendData = nullptr;

    pkgBuffer.Initialize(2048);
    assert(nullptr != pkgBuffer.pBuffer);
}

TransferWorker::~TransferWorker()
{
    stop();
    pkgBuffer.Destroy();
}

void TransferWorker::start()
{
    if (useSsl)
        tcpSocket = new QSslSocket(this);
    else
        tcpSocket = new QTcpSocket(this);

    connect(tcpSocket, &QTcpSocket::connected, this, &TransferWorker::connected);
    connect(tcpSocket, &QTcpSocket::readyRead, this, &TransferWorker::readyRead);
    connect(tcpSocket, &QTcpSocket::disconnected, this, &TransferWorker::disconnected);

    if (useSsl)
    {
        connect((QSslSocket*)tcpSocket, &QSslSocket::encrypted, this, &TransferWorker::encrypted);
        connect((QSslSocket*)tcpSocket, &QSslSocket::peerVerifyError, this, &TransferWorker::peerVerifyError);
        connect((QSslSocket*)tcpSocket, &QSslSocket::modeChanged, this, &TransferWorker::modeChanged);
        ((QSslSocket*)tcpSocket)->connectToHostEncrypted(svrIp, svrPort);
    }
    else
        tcpSocket->connectToHost(QHostAddress(svrIp), svrPort);
}

void TransferWorker::stop()
{
    if (nullptr != sendData)
    {
        filePtr->unmap(sendData);
        sendData = nullptr;
    }

    if (nullptr != tcpSocket)
    {
        tcpSocket->disconnectFromHost();
        tcpSocket->close();
    }
}

void TransferWorker::connected()
{
    if (!useSsl)
    {
        qDebug() << "tcpSocket::connected";
        protocol prot{0xA5C3, 13, "IodpModule", 0, 0x5A3C};
        qint64 sent = tcpSocket->write((char*)&prot, sizeof(prot));
        qDebug("tcpSocket[%d]->write:%ld", index, sent);
    }
}

void TransferWorker::readyRead()
{
    bool wholePkg = false;
    int64_t readByte = 0;

    if (0 == pkgBuffer.nPkgLen)
    {
        readByte = tcpSocket->read((char*)pkgBuffer.WritePos(), sizeof(uint16_t) - pkgBuffer.nCurLen);
        pkgBuffer.nCurLen += readByte;

        if (pkgBuffer.nCurLen >= sizeof(uint16_t))
        {
            pkgBuffer.nPkgLen = *(uint16_t*)pkgBuffer.pBuffer;
            pkgBuffer.nCurLen = 0;
        }
    }

    if (0 < pkgBuffer.nPkgLen)
    {
        uint16_t needByte = pkgBuffer.nPkgLen - pkgBuffer.nCurLen;
        readByte = tcpSocket->read((char*)pkgBuffer.WritePos(), needByte);

        if (readByte >= needByte)
            wholePkg = true;
    }

    if (wholePkg)
    {
        packageProcess();
    }
}

void TransferWorker::disconnected()
{
    qDebug("tcpSocket[%d] disconnected", index);
}

void TransferWorker::encrypted()
{
    qDebug() << "QSslSocket::encrypted";
    protocol prot{0xA5C3, 13, "IodpModule", 0, 0x5A3C};
    qint64 sent = tcpSocket->write((char*)&prot, sizeof(prot));
    qDebug("tcpSocket[%d]->write:%ld", index, sent);
}

void TransferWorker::peerVerifyError(const QSslError &error)
{
    qDebug() << "QSslSocket::peerVerifyError = " << error;
}

void TransferWorker::modeChanged(QSslSocket::SslMode newMode)
{
    qDebug() << "QSslSocket::modeChanged = " << newMode;
}

void TransferWorker::receiveMsg(quint16 indx, quint16 msg, qint64 start, quint32 length)
{
    uint16_t order = indx;
    uint16_t message = msg;
    int64_t offset = start;
    uint32_t len = length;

    if (index != order)
        return;

    switch ((WorkerMsg)message)
    {
    case WorkerMsg::SendFileData:
    {
        if (nullptr != sendData)
        {
            filePtr->unmap(sendData);
            sendData = nullptr;
        }

        qDebug("send start:%ld,length:%d", offset, len);
        if (offset >= 0 && len > 0)
        {
            blockOffset = offset;
            blockLength = len;
            curLength = 0;

            if (nullptr != filePtr)
            {
                fileLock->lock();
                sendData = filePtr->map(offset, len);
                fileLock->unlock();
            }
            else
                qDebug() << "Error: (nullptr == filePtr)";

            if (nullptr != sendData)
            {
                // 请求传输文件数据 // uploadFlag, start, length
                fileBlockRequest(pkgBuffer.pBuffer, offset, len);
            }
            else
            {
                qDebug() << filePtr->error();
            }
        }
        else
        {
            // 传输完毕，退出线程
            transferFinish(pkgBuffer.pBuffer);
        }
        break;
    }

    default:
        break;
    }
}

bool TransferWorker::packageProcess()
{
    bool result = false;
    uint8_t *ptr = pkgBuffer.pBuffer;
    DataCmd cmd = *(DataCmd*)ptr;

    ptr += sizeof(DataCmd);
    uint16_t len = pkgBuffer.nPkgLen - sizeof(DataCmd);

    switch (cmd)
    {
    case DataCmd::HandShake:
    {
        if (*(bool*)ptr)
        {
            qDebug("tcpSocket[%d] Handshake success.", index);

            // 向服务器发送：添加发送客户端
            result = addUploader(pkgBuffer.pBuffer);
        }
        break;
    }

    case DataCmd::Module:
        break;

    case DataCmd::FileUpload:
        break;

    case DataCmd::AddUploader:
        emit(sendMsg(index, (quint16)WorkerMsg::GetFileData));
        result = true;
        break;

    case DataCmd::FileBlock:
        result = fileBlockResponse(ptr, len);
        break;

    default:
        break;
    }

    pkgBuffer.Reset();

    return result;
}

bool TransferWorker::addUploader(uint8_t *buffer)
{
    int length = 0;
    uint8_t *ptr = buffer;
    ptr += sizeof(uint16_t);

    *(DataCmd*)ptr = DataCmd::AddUploader;
    ptr += sizeof(DataCmd);
    length += sizeof(DataCmd);

    *(uint64_t*)ptr = uploadFlag;
    ptr += sizeof(uint64_t);
    length += sizeof(uint64_t);

    ptr = buffer;
    *(uint16_t*)ptr = length;
    length += sizeof(uint16_t);

    qint64 sent = 0;

    do
    {
        sent = tcpSocket->write((char*)ptr, length);
        ptr += sent;
        length -= sent;
    } while (length > 0);

    return true;
}

bool TransferWorker::fileBlockRequest(uint8_t *buffer, qint64 start, quint32 dataLen)
{
    int length = 0;
    uint8_t *ptr = buffer;
    ptr += sizeof(uint16_t);

    *(DataCmd*)ptr = DataCmd::FileBlock;
    ptr += sizeof(DataCmd);
    length += sizeof(DataCmd);

    *(int64_t*)ptr = start;
    ptr += sizeof(int64_t);
    length += sizeof(int64_t);

    *(uint32_t*)ptr = dataLen;
    ptr += sizeof(uint32_t);
    length += sizeof(uint32_t);

    ptr = buffer;
    *(uint16_t*)ptr = length;
    length += sizeof(uint16_t);

    qint64 sent = 0;

    do
    {
        sent = tcpSocket->write((char*)ptr, length);
        ptr += sent;
        length -= sent;
    } while (length > 0);

    return true;
}

bool TransferWorker::fileBlockResponse(uint8_t *data, uint16_t size)
{
    uint8_t *ptr = data;
    bool reslut = *(bool*)ptr;
    ptr += sizeof(bool);

    if (!reslut)
        return false;

    uint16_t nPkgSize = 0;
    uint32_t blockSize = blockLength;
    uint16_t sendLen = 0;
    uint16_t headLen = 0;
    qint64 sent = 0;

    while (blockSize > 0)
    {
        nPkgSize = 0;
        headLen = sizeof(uint16_t) + sizeof(DataCmd);

        ptr = pkgBuffer.pBuffer;
        ptr += sizeof(uint16_t);

        *(DataCmd*)ptr = DataCmd::FileData;
        ptr += sizeof(DataCmd);
        nPkgSize += sizeof(DataCmd);

        sendLen = PACKAGE_SIZE_MAX - headLen;

        if (blockSize < sendLen)
            sendLen = blockSize;

        memcpy(ptr, sendData+curLength, sendLen);
        nPkgSize += sendLen;

        ptr = pkgBuffer.pBuffer;
        *(uint16_t*)ptr = nPkgSize;
        nPkgSize += sizeof(uint16_t);
        sent = 0;

        do
        {
            sent = tcpSocket->write((char*)ptr, nPkgSize);
            ptr += sent;
            nPkgSize -= sent;
        } while (nPkgSize > 0);

        blockSize -= sendLen;
        curLength += sendLen;
    }

    qDebug("one block finish.");
    filePtr->unmap(sendData);
    sendData = nullptr;
    emit(sendMsg(index, (quint16)WorkerMsg::GetFileData));

    return true;
}

bool TransferWorker::transferFinish(uint8_t *buffer)
{
    int length = 0;
    uint8_t *ptr = buffer;
    ptr += sizeof(uint16_t);

    *(DataCmd*)ptr = DataCmd::TransferFinish;
    ptr += sizeof(DataCmd);
    length += sizeof(DataCmd);

    *(bool*)ptr = true;
    ptr += sizeof(bool);
    length += sizeof(bool);

    ptr = buffer;
    *(uint16_t*)ptr = length;
    length += sizeof(uint16_t);

    qint64 sent = 0;

    do
    {
        sent = tcpSocket->write((char*)ptr, length);
        ptr += sent;
        length -= sent;
    } while (length > 0);

    emit(sendMsg(index, (quint16)WorkerMsg::TransferFinish));

    return true;
}
